package com.zeddy.android.HarrisVoice;

/*
 * @Filename: 	HarrisBlueTransport.java
 * @Classname: 	HarrisBlueTransport
 * @Author:		i.z.chirombe@wustl.edu
 * 
 * @Description:	This class is just a library class, whose purpose is to facilitates
 * 				Network programming over the Bluetooth Interface. Based on your needs, 
 * 				you can instantiate either a server or client nodes, but not both on a 
 * 				single device. 
 * 
 * 				The client side, assumes your devices are already paired, otherwise 
 * 				it will not be able to go beyond this point. So, make sure you devices
 * 				are already paired before you attempt to use this library.
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

/**
 * @author zeddy.chirombe
 *
 */
public class HarrisBlueTransport {
	public enum BluetoothErrors { SUCCESS, INACTIVE, ACCEPT_FAILED, IOEXCEPTION,
		SECURITY_EXCEPTION, SETUP_EXCEPTION, ZERO_BYTE_READ, UNDEFINED_TRANS, 
		BTH_NOPAIRED_DEVICES};
	private static boolean DEBUG = true;
	private static final String TAG = "BTH:";
	private static final String btNAME = "HarrisBlueTransport";
	private static final UUID btUUID = UUID.fromString("b7746a40-c758-4868-aa19-7ac6b3475dfc");
	private BluetoothServerSocket btServerSocket = null;
	private BluetoothAdapter btBluetoothAdapter = null;
	private BluetoothDevice btDevice = null;
	private BluetoothSocket mySocket = null;
	private Handler activityHandler = null;
	private boolean activeTransport = false;
	private boolean btServerMode = true;
	
	private Scanner 	ioInputc = null;
	private BufferedReader ioInput = null;
	private PrintWriter ioOutput = null;
	
	private TextView logView = null;
	
	public HarrisBlueTransport (Handler _handler, TextView _logView) {
		activityHandler = _handler;
		logView = _logView;
	}
	
	public void showPost(final String msg) {
		if(DEBUG) { Log.e(TAG,msg);}
		activityHandler.post(new Runnable() {
			@Override
			public void run() {
				logView.setText(msg+System.getProperty("line.separator")+logView.getText());
			}	
		});
	}
 
    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
        	btServerSocket.close();
        } catch (IOException e) { }
    }
	 
	/*
	 * AndroidTransport starts here -------
	*/
	public BluetoothErrors transSetup() {
		// set up Server TCP connection - 
		activeTransport = false;
		try {
	        // Get the local Bluetooth adapter
	        btBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	        
	    	//start discovery mechanism - 
	        Set<BluetoothDevice> pairedDevices = btBluetoothAdapter.getBondedDevices();
	    	if(pairedDevices.size() <= 0) {
	    		showPost("No Paired Devices Found....");
	    		return BluetoothErrors.BTH_NOPAIRED_DEVICES;
	    	}
	    	showPost("Found Paired Devices ...");
	        Iterator<BluetoothDevice> iter = pairedDevices.iterator();
	        if(iter.hasNext()) {
	        	btDevice = (BluetoothDevice)iter.next();
	            //Log.e(TAG,btDevice.getName() + "\n" + btDevice.getAddress());
	            showPost("BT-Paired: "+btDevice.getName() + "\n\t" + btDevice.getAddress());
	    	} 
			// MY_UUID is the app's UUID string, also used by the client code
			btServerSocket = btBluetoothAdapter.listenUsingRfcommWithServiceRecord(btNAME, btUUID);
			return BluetoothErrors.SUCCESS;
		} catch (SecurityException ex) {
			showPost("Failed to Open BlueT Channel ..."+ex);
			return BluetoothErrors.SECURITY_EXCEPTION;
		} catch (IOException ex) {
			showPost("Failed to Open BlueT Channel ..."+ex);
		}
		return BluetoothErrors.IOEXCEPTION;
	}
	
	public BluetoothErrors listen() {
		showPost("Android Transport - OK");
		showPost("Listening for client connection....");
		try {
			mySocket = btServerSocket.accept();
			if(mySocket == null) {
				showPost("BTH-Accept() Failed.");
				return BluetoothErrors.ACCEPT_FAILED;
			}
			if(ioInput!= null) ioInput = null;
			if(ioOutput!= null) ioOutput = null;		
			ioInput=new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
			ioOutput = new PrintWriter(mySocket.getOutputStream(), false);
			activeTransport = true;
        	String devInfo = "BT-Accept() - OK\n\tDevAddr: "+mySocket.getRemoteDevice().getAddress();
        	showPost(devInfo);
        	btServerMode = true;
			return BluetoothErrors.SUCCESS;	
		} catch (IOException ex) {
			showPost("BTH: Error: listen() - "+ex);
		}
		return BluetoothErrors.IOEXCEPTION;
	}
	
	public BluetoothErrors connect() {
		//btBluetoothAdapter.cancelDiscovery();
		try {			
			mySocket = btDevice.createRfcommSocketToServiceRecord(btUUID);
			mySocket.connect();
			showPost("BT: Found Server & Connected - OK");

			// setup io interface - 
			ioInputc = new Scanner(mySocket.getInputStream());
            ioOutput= new PrintWriter(mySocket.getOutputStream(), false); 
            btServerMode = false;
            activeTransport = true;
            return BluetoothErrors.SUCCESS; 
		} catch (IOException e) {
        	showPost("Failed to Open Client BT-Transport ...\n"+e);
    	}
		return BluetoothErrors.IOEXCEPTION;
	}	
	
	public BluetoothErrors recv_msg(HarrisMessage hMsg) {
		// Keep listening until exception or socket - 
		if(!activeTransport) {
			showPost("Transport inactive...exiting");
			return BluetoothErrors.INACTIVE;
		}
		try {
			showPost("...awaiting-new-msg...");
			if(mySocket != null) {
				hMsg.reset();
				String msg;
				if(btServerMode) {
					msg = ioInput.readLine();
				} else {
					msg = ioInputc.nextLine();
				}
				if(msg.equals("null")) {
					//showPost("dbg - zerobyte");
					return BluetoothErrors.ZERO_BYTE_READ;
				}
				if(hMsg.parse_list(msg)) {
					//showPost("dgb - success");
					return BluetoothErrors.SUCCESS;
				}
				showPost("Malformed Msg[ "+msg+" ] ");
			}
		} catch (IOException e) {
			showPost("Error-reading BTH-socket ...\n");
			activeTransport = false;
			mySocket = null;
    	} catch (Exception e) {
			showPost("Error-reading BTH-socket ...\n");
			activeTransport = false;
			mySocket = null;
    	}
		return BluetoothErrors.IOEXCEPTION;
	}

	public void post_msg(final String reply) {
		activityHandler.post(new Runnable() {
			@Override
			public void run() {
				if(mySocket != null) {
					try {
						ioOutput.write(reply+"\n");
						ioOutput.flush();
						//pw.write("+Yes, I can help, but you will have to pay.\n");
					} catch (NullPointerException e) {
						showPost("postReply() "+e);
						activeTransport = false;
					} catch (Exception e) {
						showPost("postReply() "+e);
						activeTransport = false;
					}
				}
				showPost("....trans-stop....");
			}	
		});	
	}
	public void send_msg(final String reply) {
		if(mySocket != null) {
			try {
				ioOutput.write(reply+"\n");
				ioOutput.flush();
				ioOutput.flush();
				//pw.write("+Yes, I can help, but you will have to pay.\n");
			} catch (NullPointerException e) {
				showPost("postReply() "+e);
				activeTransport = false;
			} catch (Exception e) {
				showPost("postReply() "+e);
				activeTransport = false;
			}
		}
		showPost("....trans-stop....");
	}
}

